#include "StdAfx.h"


#ifndef EXCLUDE_SCALEFORM_SDK


#include "GAllocatorCryMem.h"
#include "SharedStates.h"
#include "SharedResources.h"


#if defined(PS3) || (!defined(_DEBUG) && !defined(NOT_USE_CRY_MEMORY_MANAGER))

static inline size_t GetSize(void* pMem)
{
#	if !defined(PS3)
	return CryGetMemSize(pMem, 0);


#	endif
}

#	define TRACK_ALLOCATIONS

#endif // #if defined(PS3) || (!defined(_DEBUG) && !defined(NOT_USE_CRY_MEMORY_MANAGER))


//////////////////////////////////////////////////////////////////////////
// GSysAllocCryMem

GSysAllocCryMem::GSysAllocCryMem(size_t addressSpaceSize)
{
	memset(&m_stats, 0, sizeof(m_stats));
	m_pHeap = CryGetIMemoryManager()->CreatePageMappingHeap(addressSpaceSize, "Scaleform");
	m_tempArena.SetAlloc(this);
}

GSysAllocCryMem::~GSysAllocCryMem()
{
	m_tempArena.SetAlloc(0);
	m_pHeap->Release();
}

void GSysAllocCryMem::GetInfo(Info* i) const
{
	if (i)
	{
		size_t granularity = m_pHeap->GetGranularity();
		i->MinAlign = granularity;
		i->MaxAlign = granularity;
		i->Granularity = max((int)MinGranularity, (int)granularity);
		i->HasRealloc = false;
	}
}


void* GSysAllocCryMem::Alloc(UPInt size, UPInt align)
{
	ScopedSwitchToGlobalHeap useGlobalHeap;

	size_t granularity = m_pHeap->GetGranularity();
	size = (size + granularity - 1) & ~(granularity - 1);

	void* ptr = m_pHeap->Map(size);

#ifndef RELEASE
	if (ptr == NULL)
	{
		CryFatalError("Could not allocate %u bytes of memory for Scaleform, limit is exceeded! Please adjust \"sys_flash_address_space\" or reduce memory footprint of you flash assets!", size);
		return NULL;
	}
#endif

#ifndef GHEAP_TRACE_ALL
	MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CryMalloc);
	MEMREPLAY_SCOPE_ALLOC(ptr, size, granularity);
#endif

#if defined(TRACK_ALLOCATIONS)
	++m_stats.AllocCount;
	if (ptr)
		m_stats.Allocated += size;
#endif



	return ptr;
}


bool GSysAllocCryMem::Free(void* ptr, UPInt size, UPInt align)
{
#if defined(TRACK_ALLOCATIONS)
	++m_stats.FreeCount;
	if (ptr)
		m_stats.Allocated -= size;
#endif





#ifndef GHEAP_TRACE_ALL
	MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CryMalloc);
	MEMREPLAY_SCOPE_FREE(ptr);
#endif

	m_pHeap->Unmap(ptr, size);

	return true;
}


CryGFxMemInterface::Stats GSysAllocCryMem::GetStats() const
{
	return m_stats;
}


void GSysAllocCryMem::GetMemoryUsage(ICrySizer* pSizer) const
{




	pSizer->AddObject(this, sizeof(*this) + m_stats.Allocated);

}


GSysAllocBase* GSysAllocCryMem::GetSysAllocImpl()
{
	return this;
}


GFxTempMemoryArenaWrapper& GSysAllocCryMem::GetTempMemoryArena()
{
	return m_tempArena;
}


//////////////////////////////////////////////////////////////////////////
// GSysAllocStaticCryMem

class GSysAllocStaticChecked : public GSysAllocStatic
{
public:
	virtual void* Alloc(UPInt size, UPInt align)
	{
		void* p = GSysAllocStatic::Alloc(size, align);
		assert(p);
		if (!p)
		{
			CryGFxLog::GetAccess().LogError("Allocation request for %d bytes in static pool failed! (pool size %.1f MB, %.1f MB used / %.1f MB footprint).",
				size, GetSize() / (1024.0f * 1024.0f), GetUsedSpace() / (1024.0f * 1024.0f), GetFootprint() / (1024.0f * 1024.0f));
		}
		return p;
	}

public:
	GSysAllocStaticChecked(void* p0 = 0, UPInt size0 = 0, void* p1 = 0, UPInt size1 = 0, void* p2 = 0, UPInt size2 = 0, void* p3 = 0, UPInt size3 = 0)
	: GSysAllocStatic(p0, size0, p1, size1, p2, size2, p3, size3) {}

	~GSysAllocStaticChecked() {}
};


GSysAllocStaticCryMem::GSysAllocStaticCryMem(size_t poolSize)
: m_pStaticAlloc(0)
, m_pMem(0)
, m_size(poolSize)
, m_tempArena()
{
	ScopedSwitchToGlobalHeap useGlobalHeap;






	m_pMem = malloc(m_size);

	if (m_pMem)
	{
		m_pStaticAlloc = new GSysAllocStaticChecked(m_pMem, m_size);
		m_tempArena.SetAlloc(m_pStaticAlloc);
	}
}


GSysAllocStaticCryMem::~GSysAllocStaticCryMem()
{
	m_tempArena.SetAlloc(0);
	SAFE_DELETE(m_pStaticAlloc);
	if (m_pMem)
	{
		free(m_pMem);
		m_pMem = 0;
	}
}


CryGFxMemInterface::Stats GSysAllocStaticCryMem::GetStats() const
{
	Stats stats;
	stats.AllocCount = 1;
	stats.FreeCount = 0;
	stats.Allocated = m_size;
	return stats;
}


void GSysAllocStaticCryMem::GetMemoryUsage(ICrySizer* pSizer) const
{
	pSizer->AddObject(this, sizeof(*this));
	pSizer->AddObject(m_pStaticAlloc, sizeof(*this));
	pSizer->AddObject(m_pMem, m_size);
}


GSysAllocBase* GSysAllocStaticCryMem::GetSysAllocImpl()
{
	assert(m_pStaticAlloc);
	return m_pStaticAlloc;
}


GFxTempMemoryArenaWrapper& GSysAllocStaticCryMem::GetTempMemoryArena()
{
	return m_tempArena;
}


//////////////////////////////////////////////////////////////////////////
// GFxTempMemoryArenaWrapper

int GFxTempMemoryArenaWrapper::ms_sys_flash_use_temp_arena(1);

void GFxTempMemoryArenaWrapper::InitCVars()
{
#if defined(_DEBUG)
	{
		static bool s_init = false;
		assert(!s_init);
		s_init = true;
	}
#endif

	REGISTER_CVAR2("sys_flash_use_temp_arena", &ms_sys_flash_use_temp_arena, 1, 
		0, "Enables creation of temporary instances through designated memory arena.");
}


GFxTempMemoryArenaWrapper::GFxTempMemoryArenaWrapper()
: m_lock()
, m_pAlloc(0)
, m_tempArenaActive(false)
{
}


GFxTempMemoryArenaWrapper::~GFxTempMemoryArenaWrapper()
{
	assert(!m_tempArenaActive);
}


int GFxTempMemoryArenaWrapper::Create()
{
	CryAutoCriticalSection lock(m_lock);

	if (!m_tempArenaActive && m_pAlloc && ms_sys_flash_use_temp_arena)
	{
		GMemory::CreateArena(1, this);
		m_tempArenaActive = true;
	}

	return m_tempArenaActive ? 1 : 0;
}


void GFxTempMemoryArenaWrapper::Destroy()
{
	CryAutoCriticalSection lock(m_lock);

	if (m_tempArenaActive && GMemory::ArenaIsEmpty(1))
	{
		GMemory::DestroyArena(1);
		m_tempArenaActive = false;

		CSharedFlashPlayerResources::GetAccess().ResetMeshCache();
	}
}


void GFxTempMemoryArenaWrapper::SetAlloc(GSysAllocPaged* pAlloc)
{
	assert(!pAlloc || !m_pAlloc);
	m_pAlloc = pAlloc;
}


void GFxTempMemoryArenaWrapper::GetInfo(Info* i) const
{
	assert(m_pAlloc);
	m_pAlloc->GetInfo(i);
}


void* GFxTempMemoryArenaWrapper::Alloc(UPInt size, UPInt align)
{
	assert(m_pAlloc);
	return m_pAlloc->Alloc(size, align);
}


bool GFxTempMemoryArenaWrapper::Free(void* ptr, UPInt size, UPInt align)
{
	assert(m_pAlloc);
	return m_pAlloc->Free(ptr, size, align);
}


#endif // #ifndef EXCLUDE_SCALEFORM_SDK